package com.hc.springsecurity;

import com.hc.entity.Permission;
import com.hc.entity.Role;
import com.hc.entity.User;
import com.hc.service.PermissionService;
import com.hc.service.RoleService;
import com.hc.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * SpringSecurity内部自动匹配密码是否正确时，密码一定要加密解密处理，要求Spring容器中必须
 * 存在一个PasswordEncoder实现类的对象，在这个对象中提供加密和解密逻辑，其中解密逻辑不是要
 * 求反向计算明文，而是要求实现明文和密码的验证（解密验证可以，加密验证也行）
 */
@Slf4j
@Service
public class MyUserDetailsService implements UserDetailsService {

    @Resource
    private UserService userService;
    @Resource
    private RoleService roleService;
    @Resource
    private PermissionService permissionService;


    /**
     * 认证方法
     *
     * @param username 登录用户的用户名
     * @return
     * @throws UsernameNotFoundException 当用户查询失败时抛出该异常
     *                                   <p>
     *                                   登录请求参数命名规则：用户名必须是username，密码必须是password，此
     *                                   逻辑由UsernamePasswordAuthenticationFilter决定
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        log.info("自定义用户认证逻辑，传递的用户名是： " + username);
        User user = userService.getUserByUsername(username);
        if (user == null) {
            //查询失败，用户名不存在，抛出异常
            throw new UsernameNotFoundException("用户名或密码错误");
        }
        log.info("自定义用户认证逻辑，登录用户是： " + user);

        //用户登录成功后，查询用户的角色和权限
        //角色集合
        List<Role> roles = roleService.getRolesByUserId(user.getId());
        //权限集合
        List<Permission> permissions = permissionService.getPermissionsByUserId(user.getId());

        //在SpringSecurity中，对角色的命名有严格的规则，要求角色名称的前缀必须是ROLE_
        //一定不要在数据库中存储ROLE_，数据库中存储具体的角色名，查询出来后采用拼串的方式即可
        ///在SpringSecurity中，角色和权限是平等的，都叫Authority，都 代表用户的访问权限

        Set<SimpleGrantedAuthority> authorities = new HashSet<>(); //采用Set可以去重
        roles.forEach(item->{
            authorities.add(new SimpleGrantedAuthority("ROLE_"+item.getCode()));
        });
        permissions.forEach(item->{
            authorities.add(new SimpleGrantedAuthority(item.getCode()));
        });
        log.info("用户 "+username+"的权限集合是："+authorities);

authorities.forEach(item-> System.out.println(item.getAuthority()));  //测试代码，需要删除

        //匹配密码是否正确，匹配动作由SpringSecurity自动完成
        //返回结果是UserDetails类型的实现类，可以自定义、也可以使用SpringSecurity框架提供的实现类
        org.springframework.security.core.userdetails.User result = new org.springframework.security.core.userdetails.User(
                username,               //用户身份
                user.getPassword(),     //用户正确密码
                authorities
        );
        return result;
    }
}
